﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Gezel.Model;
using Weazel.Gezel.Model.Errors;

#endregion

namespace Weazel.Gezel.Parser
{
  /// <summary>
  /// FsmController :
  ///    FSM ControllerName LEFT_PARENTHESIS DatapathName RIGHT_PARENTHESIS 
  ///    LEFT_BRACE FsmDefinition RIGHT_BRACE
  /// 
  /// FsmController :
  ///    FSM Identifer LEFT_PARENTHESIS Identifer RIGHT_PARENTHESIS 
  ///    LEFT_BRACE FsmDefinition RIGHT_BRACE
  /// </summary>
  public class ParseFsm
  {
    public static readonly Set<Token> First = new Set<Token>(
      Token.Fsm
    );

    public static void Parse(Set<Token> follow, Tokenizer t, Gezel.Model.Model parent)
    {
      ParserDebug.WriteLine(
        "Entering 'ParseFsm'",
        ConsoleColor.Red
      );

      Set<Token> current = new Set<Token>(
        // Token.Fsm,
        Token.Identifier,
        Token.LeftParenthesis,
        Token.RightParenthesis,
        Token.LeftBrace,
        Token.RightBrace
      );

      current = current.Union(ParseFsmDefinition.First);

			Position startPosition = t.CurrentPosition;

      t.Accept(Token.Fsm, follow.Union(current));

      string name = (t.CurrentToken == Token.Identifier) ? t.CurrentString : "";

      current.Remove(Token.Identifier);
      t.Accept(Token.Identifier, follow.Union(current));

      current.Remove(Token.LeftParenthesis);
      t.Accept(Token.LeftParenthesis, follow.Union(current));

      string datapath = (t.CurrentToken == Token.Identifier) ? t.CurrentString : "";

      t.Accept(Token.Identifier, follow.Union(current));

      current.Remove(Token.RightParenthesis);
      t.Accept(Token.RightParenthesis, follow.Union(current));

      // create the controller object
      Controller controller = new Controller(parent, name, startPosition, null);
      controller.DatapathRef = datapath;
      controller.Type = Controller.ControllerType.Fsm;

      current.Remove(Token.LeftBrace);
      t.Accept(Token.LeftBrace, follow.Union(current));

      current.Remove(ParseFsmDefinition.First);
      ParseFsmDefinition.Parse(follow.Union(current), t, controller);

			controller.EndPosition = t.CurrentEndPosition;

      current.Remove(Token.RightBrace);
      t.Accept(Token.RightBrace, follow);

      if (name != null && datapath != null)
      {
        if (parent.Controllers.ContainsKey(name))
        {
          t.Errors.Add(
            new DuplicateControllerNameParseError(
              controller,
              parent.Controllers[name]
            )
          );
        }
        else
        {
          parent.Controllers.Add(name, controller);
        }
      }

      ParserDebug.WriteLine(
        "Leaving 'ParseFsm'",
        ConsoleColor.Yellow
      );
    }

    public class ParseFsmDefinition 
    {
      public static readonly Set<Token> First =
        ParseFsmDefinitionElement.First;

      public static void Parse(Set<Token> follow, Tokenizer t, Controller controller)
      {
        ParserDebug.WriteLine(
          "Entering 'ParseFsmDefinition'",
          ConsoleColor.Red
        );

        bool done = false;

        do
        {
          t.Check(follow.Union(ParseFsmDefinitionElement.First));

          if (ParseFsmDefinitionElement.First.Contains(t.CurrentToken))
          {
            ParseFsmDefinitionElement.Parse(follow, t, controller);
          }
          else
          {
            done = true;
          }

        } while (!done);

        ParserDebug.WriteLine(
          "Leaving 'ParseFsmDefinition'",
          ConsoleColor.Yellow
        );
      }
    }

    public class ParseFsmDefinitionElement
    {
      public static readonly Set<Token> First
        = new Set<Token>().Union(ParseStateId.First).Union(ParseTransitionId.First);

      public static void Parse(Set<Token> follow, Tokenizer t, Controller controller)
      {
        ParserDebug.WriteLine(
          "Entering 'ParseFsmDefinitionElement'",
          ConsoleColor.Red
        );

        Set<Token> current = new Set<Token>();
        current = current.Union(ParseStateId.First);
        current = current.Union(ParseTransitionId.First);

        // branch

        t.Check(follow.Union(current));

        if (ParseStateId.First.Contains(t.CurrentToken))
        {
          ParseStateId.Parse(follow, t, controller);
        }
        else if (ParseTransitionId.First.Contains(t.CurrentToken))
        {
          ParseTransitionId.Parse(follow, t, controller);
        }
        else
        {
          t.Errors.Add(
            new UnexpectedTokenParseError(
              t.CurrentPosition,
              t.CurrentToken,
              follow.Union(current)
            )
          );
        }

        ParserDebug.WriteLine(
          "Leaving 'ParseFsmDefinitionElement'",
          ConsoleColor.Yellow
        );
      }
    }

    /// <summary>
    /// StateId :
    ///     STATE StateIdList SEMICOLON
    ///   : INITIALSTATE InitialStateId SEMICOLON;
    /// 
    /// StateId : 
    ///     STATE StateIdList SEMICOLON
    ///   : INITIALSTATE Identifier SEMICOLON;
    /// </summary>
    public class ParseStateId
    {
      public static readonly Set<Token> First = new Set<Token>(
        Token.State,
        Token.Initial
      );

      public static void Parse(Set<Token> follow, Tokenizer t, Controller controller)
      {
        ParserDebug.WriteLine(
          "Entering 'ParseStateId'",
          ConsoleColor.Red
        );

        Set<Token> current = new Set<Token>(        
          Token.State,
          Token.Initial,
          Token.Identifier,
          Token.Semicolon,
          Token.Identifier
        );

        current = current.Union(ParseStateIdList.First);
				
				Position startPosition = t.CurrentPosition;

        t.Check(follow.Union(current));
       
        if (t.CurrentToken == Token.State)
        {
          current.Remove(Token.State, Token.Initial, Token.Identifier);
          t.Accept(Token.State, follow.Union(current));

          List<string> states = ParseStateIdList.Parse(follow.Union(Token.Semicolon), t);

          foreach (string stateName in states)
          {
            State state = new State(controller, stateName, startPosition, null);

            if (controller.States.ContainsKey(stateName))
            {
              ErrorMessage message = new ErrorMessage();
              message.AddString("Duplicate defintion of state '");
              message.AddEntity(state);
              message.AddString("' in controller '");
              message.AddEntity(controller);
              message.AddString("' at ");
              message.AddPosition(controller.StartPosition);

              t.Errors.Add(new ParseError(ParseErrorType.DuplicateStateDefintion, message));
            }
            else if (controller.InitialState != null && controller.InitialState.LocalName == stateName)
            {
              // also decleared in states list
              ErrorMessage message = new ErrorMessage();
              message.AddString("Initial state '");
              message.AddEntity(controller.InitialState);
              message.AddString("' also declared in state list at ");
              message.AddPosition(startPosition);
              message.AddString(".");

              t.Errors.Add(
                new ParseError(
                  ParseErrorType.DuplicateStateDefintion,
                  message
                )
              );
            }
            else
            {
              controller.States.Add(stateName, state);
            }

						state.EndPosition = t.CurrentEndPosition;
          }
					
          t.Accept(Token.Semicolon, follow);
        }
        else if (t.CurrentToken == Token.Initial)
        {                              
          current.Remove(Token.State, Token.Initial);
          current.Remove(ParseStateId.First);

          t.Accept(Token.Initial, follow.Union(current));

          if (t.CurrentToken == Token.Identifier)
          {
            State initialState = new State(controller, t.CurrentString, startPosition, null);

            if (controller.InitialState != null)
            {
              // initial state already set!
              ErrorMessage message = new ErrorMessage();
              message.AddString("Duplicate initial state found at ");
              message.AddPosition(initialState.StartPosition);
              message.AddString(". Only one initial state allowed for each finite statemachine controller.");

              t.Errors.Add(
                new ParseError(
                  ParseErrorType.DuplicateInitialState,
                  message
                )
              );
            }
            else if (controller.States.ContainsKey(initialState.LocalName))
            {
              // also decleared in states list
              ErrorMessage message = new ErrorMessage();
              message.AddString("Initial state '");
              message.AddEntity(initialState);  
              message.AddString("' also declared in state list at ");
              message.AddPosition(controller.States[initialState.LocalName].StartPosition);
              message.AddString(".");

              t.Errors.Add(
                new ParseError(
                  ParseErrorType.DuplicateStateDefintion,
                  message
                )
              );
            }
            else
            {              
              controller.States.Add(t.CurrentString, initialState);
              controller.InitialState = initialState;
            }

						initialState.EndPosition = t.CurrentEndPosition;
          }
          
          t.Accept(Token.Identifier, follow.Union(Token.Semicolon));

          t.Accept(Token.Semicolon, follow);
        }
        else
        {
          t.Errors.Add(
            new UnexpectedTokenParseError(
              t.CurrentPosition,
              t.CurrentToken,
              follow.Union(current)
            )
          );
        }

        ParserDebug.WriteLine(
          "Leaving 'ParseStateId'",
          ConsoleColor.Yellow
        );
      }
    }

    /// <summary>
    /// TransitionId :
    ///   AT StateFromId TargetTransitionDescription
    /// 
    /// TransitionId :
    ///   AT Identifier TargetTransitionDescription
    /// </summary>
    public class ParseTransitionId
    {
      public static readonly Set<Token> First = new Set<Token>(
        Token.At
      );

      public static void Parse(Set<Token> follow, Tokenizer t, Controller controller)
      {
        ParserDebug.WriteLine(
          "Entering 'ParseTransitionId'",
          ConsoleColor.Red
        );

        Set<Token> current = new Set<Token>(
          Token.Identifier         
        );

        current.Union(ParseTargetTransitionDescription.First);

        Position position = t.CurrentPosition;

        t.Accept(Token.At, follow.Union(current));

        string sourceState = (t.CurrentToken == Token.Identifier) ? t.CurrentString : "";

        t.Accept(Token.Identifier, follow.Union(ParseTargetTransitionDescription.First));

        Transition transition = ParseTargetTransitionDescription.Parse(follow, t, sourceState, controller);

        // add iff ok
        if (transition != null)
        {
          // only one transtion allowed from each state..
          if (controller.Transitions.ContainsKey(sourceState))
          {
            t.Errors.Add(
              new DuplicateTransitionParseError(
                transition,
                controller.Transitions[sourceState],
                sourceState,
                controller
              )
            );
          }
          else
          {
            controller.Transitions.Add(sourceState, transition);
          }
        }

        ParserDebug.WriteLine(
          "Leaving 'ParseTransitionId'",
          ConsoleColor.Yellow
        );
      }
    }

    /// <summary>
    /// StateIdList :
    ///     StateId
    ///   : StateIdList COMMA StateId;
    /// 
    /// StateIdList :
    ///     Identifier
    ///   : StateIdList COMMA Identifier;
    /// </summary>
    public class ParseStateIdList
    {
      public static readonly Set<Token> First = new Set<Token>(
        Token.Identifier
      );

      public static List<string> Parse(Set<Token> follow, Tokenizer t)
      {
        ParserDebug.WriteLine(
          "Entering 'ParseStateIdList'",
          ConsoleColor.Red
        );

        List<String> result = new List<String>();

        Set<Token> current = new Set<Token>(
          Token.Identifier,
          Token.Comma
        );

        bool done = false;

        do
        {
          if (t.CurrentToken == Token.Identifier)
            result.Add(t.CurrentString);

          t.Accept(Token.Identifier, follow.Union(current));

          t.Check(follow.Union(current));

          if (t.CurrentToken == Token.Comma)
          {
            t.Accept(Token.Comma, follow.Union(current));
          }
          else
          {
            done = true;
          }

        } while (!done);

        ParserDebug.WriteLine(
          "Leaving 'ParseStateIdList'",
          ConsoleColor.Yellow
        );

        return result;
      }
    }

//    public class ParseUnconditionalTransition 
//    {
//      public static readonly Set<Token> First = 
//        new Set<Token>().Union(ParseSimpleSfgSet.First).Union(ParseCompositeSfgSet.First);
//
//      public static void Parse(Set<Token> follow, Tokenizer t, Controller controller)
//      {
//        ParserDebug.WriteLine(
//          "Entering 'ParseUnconditionalTransition'",
//          ConsoleColor.Red
//        );
//
//        Set<Token> current = new Set<Token>(
//       //   Tok
//        );
//
//        current = current.Union(ParseSimpleSfgSet.First);
//        current = current.Union(ParseCompositeSfgSet.First);
//
//        t.Check(follow.Union(current));
//
//        if (ParseSimpleSfgSet.First.Contains(t.CurrentToken))
//        {
//          current.Remove(ParseSimpleSfgSet.First);
//          current.Remove(ParseCompositeSfgSet.First);
//
//           ParseSimpleSfgSet.Parse(follow.Union(current), t);
//        }
//        else if (ParseCompositeSfgSet.First.Contains(t.CurrentToken))
//        {
//          current.Remove(ParseSimpleSfgSet.First);
//          current.Remove(ParseCompositeSfgSet.First);
//
//          ParseCompositeSfgSet.Parse(follow.Union(current), t);
//        }
//        else
//        {
//          // todo: add error
//          throw new NotImplementedException("error message missing");
//        }
//
//        ParserDebug.WriteLine(
//          "Leaving 'ParseUnconditionalTransition'",
//          ConsoleColor.Yellow
//        );
//      }
//    }

    public class ParseSimpleSfgSet
    {
      public static readonly Set<Token> First = ParseInstructionSfgId.First;

      public static string Parse(Set<Token> follow, Tokenizer t, ref bool trace)
      {
        ParserDebug.WriteLine(
          "Entering 'ParseSimpleSfgSet'",
          ConsoleColor.Red
        );

        string result = ParseInstructionSfgId.Parse(follow, t, ref trace);

        ParserDebug.WriteLine(
          "Leaving 'ParseSimpleSfgSet'",
          ConsoleColor.Yellow
        );

        return result;
      }
    }

    /// <summary>
    /// TargetTransitionDescription : 
    ///      UnconditionalTransitionDescription 
    ///   |  IF TransitionCondition THEN TargetTransitionDescription ELSE TargetTransitionDescription;
    /// </summary>
    public class ParseTargetTransitionDescription
    {
      public static readonly Set<Token> First = 
        new Set<Token>(Token.If).Union(ParseUnconditionalTransitionDescription.First);

      public static Transition Parse(Set<Token> follow, Tokenizer t, string sourceState, Controller controller)
      {
        ParserDebug.WriteLine(
          "Entering 'ParseTargetTransitionDescription'",
          ConsoleColor.Red
        );

        Set<Token> current = new Set<Token>(
          Token.If,
          Token.Then,
          Token.Else
        );

        current = current.Union(ParseUnconditionalTransitionDescription.First);
        current = current.Union(ParseTargetTransitionDescription.First);
        current = current.Union(ParseTransitionCondition.First);
	
				Position startPosition = t.CurrentStartPosition;

        t.Check(follow.Union(current));

        if (ParseUnconditionalTransitionDescription.First.Contains(t.CurrentToken))
        {
          return ParseUnconditionalTransitionDescription.Parse(follow, t, sourceState, controller);          
        }
        else if(t.CurrentToken == Token.If)
        {
          current.Remove(Token.If);
          current.Remove(ParseUnconditionalTransitionDescription.First);

          t.Accept(Token.If, follow.Union(current));

          current.Remove(ParseTransitionCondition.First);
          Expression condition = ParseTransitionCondition.Parse(follow.Union(current), t);
          
          current.Remove(Token.Then);
          t.Accept(Token.Then, follow.Union(current));

          // dont remove TargetTrasutionDesc.First yet...
          Transition first = ParseTargetTransitionDescription.Parse(follow, t, sourceState, controller);

          current.Remove(Token.Else);
          t.Accept(Token.Else, follow.Union(current));

          Transition second = ParseTargetTransitionDescription.Parse(follow, t, sourceState, controller);

					// is this right?
					Position endPosition = t.CurrentEndPosition;

          ParserDebug.WriteLine(
            "Leaving 'ParseTargetTransitionDescription'",
            ConsoleColor.Yellow
          );

          if (first != null && second != null)
            return new Transition(
							controller, 
							condition, 
							first, 
							second, 
							sourceState, 
							startPosition,
							endPosition
						);
          else
            return null;
        }
        else
        {
          t.Errors.Add(
            new UnexpectedTokenParseError(
              t.CurrentPosition,
              t.CurrentToken,
              follow.Union(current)
            )
          );

          return null;
        }
      }
    }

    /// <summary>
    /// CompositeSfgSet :
    ///     LEFT_PARENTHESIS CompositeSfgSetElement RIGHT_PARENTHESIS
    ///   | LEFT_PARENTHESIS CompositeSfgSet COMMA CompositeSfgSetElement RIGHT_PARENTHESIS;
    /// 
    /// CompositeSfgSet :
    ///     LEFT_PARENTHESIS Identifier RIGHT_PARENTHESIS
    ///   | LEFT_PARENTHESIS CompositeSfgSet COMMA Identifer RIGHT_PARENTHESIS;
    /// </summary>
    public class ParseCompositeSfgSet
    {
      public static readonly Set<Token> First = new Set<Token>(
        Token.LeftParenthesis
      );

      public static string[] Parse(Set<Token> follow, Tokenizer t, ref bool trace)
      {
        ParserDebug.WriteLine(
          "Entering 'ParseCompositeSfgSet'",
          ConsoleColor.Red
        );

        List<string> result = new List<string>();

        Set<Token> current = new Set<Token>(
          // Token.LeftParenthesis,
          Token.Comma,
          Token.RightParenthesis
        );

        current = current.Union(ParseInstructionSfgId.First);

        t.Accept(Token.LeftParenthesis, follow.Union(current));

        bool done = false;

        do
        {
          // empty lists are not allowed. 
          if (t.CurrentToken == Token.RightParenthesis)
          {
            ErrorMessage message = new ErrorMessage();
            message.AddString("Empty signal flow graph list found at ");
            message.AddPosition(t.CurrentPosition);
            message.AddString(" in finite statemachine controller. ");
            message.AddString("Empty signal flow graph lists not allowed.");

            t.Errors.Add(new ParseError(ParseErrorType.EmptySfgList, message));

            break;
          }

          string sfg = ParseInstructionSfgId.Parse(follow.Union(current), t, ref trace);

          if (sfg != null)
            result.Add(sfg);

          t.Check(follow.Union(current));

          if (t.CurrentToken == Token.Comma)
          {
            t.Accept(Token.Comma, follow.Union(current));
          }
          else
          {
            done = true;
          }

        } while (!done);

        t.Accept(Token.RightParenthesis, follow);

        ParserDebug.WriteLine(
          "Leaving 'ParseCompositeSfgSet'",
          ConsoleColor.Yellow
        );

        return result.ToArray();
      }
    }

    public static class ParseInstructionSfgId
    {
      public static readonly Set<Token> First = new Set<Token>(
        Token.Identifier, 
        Token.TraceDirective
      );

      public static string Parse(Set<Token> follow, Tokenizer t, ref bool isTrace)
      {
        Set<Token> current = First;

        t.Check(follow.Union(current));

        if (t.CurrentToken == Token.Identifier)
        {
          string sfg = t.CurrentToken == Token.Identifier ? 
              t.CurrentString 
            : null;

          t.Accept(Token.Identifier, follow);

          isTrace = false;

          return sfg;
        }
        else if (t.CurrentToken == Token.TraceDirective)
        {
          t.Accept(Token.TraceDirective, follow);

          isTrace = true;

          return null;
        }
        else
        {
          t.Errors.Add(
            new UnexpectedTokenParseError(
              t.CurrentPosition,
              t.CurrentToken,
              follow.Union(current)
            )
          );

          return null;
        }
      }
    }

    /// <summary>
    /// UnconditionalTransitionDescription :   
    ///     SimpleSfgSet TARGET StateToId SEMICOLON
    ///   | CompositeSfgSet TARGET StateToId SEMICOLON;
    /// 
    /// UnconditionalTransitionDescription :   
    ///     Identifer TARGET Identifer SEMICOLON
    ///   | CompositeSfgSet TARGET Identifer SEMICOLON;
    /// </summary>
    public class ParseUnconditionalTransitionDescription
    {
      public static readonly Set<Token> First =
        new Set<Token>().Union(ParseCompositeSfgSet.First).Union(ParseSimpleSfgSet.First);

      public static Transition Parse(Set<Token> follow, Tokenizer t, string sourceState, Controller controller)
      {
        ParserDebug.WriteLine(
          "Entering 'ParseUnconditionalTransitionDescription'",
          ConsoleColor.Red
        );

        Set<Token> current = new Set<Token>(
          Token.Identifier,
          Token.Transition, 
          Token.Semicolon
        );

        // note: using another set for the first call to check.
        //       ParseSimpleSfgSet.First contains Identifer which should
        //       also be included in the following invocation of the two 
        //       parse methods.

        Set<Token> firstCurrent = current.Union(ParseSimpleSfgSet.First);
        firstCurrent = firstCurrent.Union(ParseCompositeSfgSet.First);
				
				Position startPosition = t.CurrentPosition;

        t.Check(follow.Union(firstCurrent));

        string[] sfgs = null;

        bool trace = false;

        if (ParseSimpleSfgSet.First.Contains(t.CurrentToken))
        {
          string sfg = ParseSimpleSfgSet.Parse(current, t, ref trace);

          if (sfg != null)
            sfgs = new string[] { sfg };
          else
            sfgs = new string[0];
        }
        else if (ParseCompositeSfgSet.First.Contains(t.CurrentToken))
        {
          sfgs = ParseCompositeSfgSet.Parse(follow, t, ref trace);
        }

        current.Remove(Token.Transition);
        t.Accept(Token.Transition, follow.Union(current));

        string target = null;
        if (t.CurrentToken == Token.Identifier)
        {
          target = t.CurrentString;
        }

        current.Remove(Token.Identifier);
        t.Accept(Token.Identifier, follow.Union(current));

				Position endPosition = t.CurrentEndPosition;

        t.Accept(Token.Semicolon, follow);

        // add sfg's to controller
        foreach (string s in sfgs)
          if (!controller.SignalFlowGraphRefs.Contains(s))
            controller.SignalFlowGraphRefs.Add(s);

        ParserDebug.WriteLine(
          "Leaving 'ParseUnconditionalTransitionDescription'",
          ConsoleColor.Yellow
        );
     
        return 
					new Transition(
						controller, 
						target, 
						sfgs, 
						trace, 
						sourceState, 
						startPosition,
						endPosition
					);
      }
    }

    /// <summary>
    /// TransitionCondition : 
    ///   LEFT_PARENTHESIS Expression RIGHT_PARENTHESIS;
    /// 
    /// Note use of ParseIorExpression. In the expression hierarchy both the
    /// ParseAssignemntExpression and ParseExpression preceeds this rule but
    /// these two are not usable as conditions.
    /// </summary>
    public class ParseTransitionCondition
    {
      public static readonly Set<Token> First = new Set<Token>(
        Token.LeftParenthesis
      );

      public static Expression Parse(Set<Token> follow, Tokenizer t)
      {
        ParserDebug.WriteLine(
          "Entering 'ParseTransitionCondition'",
          ConsoleColor.Red
        );

        Set<Token> current = new Set<Token>(
          Token.RightParenthesis
        );

        current = current.Union(ParseIorExpression.First);

        t.Accept(Token.LeftParenthesis, follow.Union(current));

        current.Remove(ParseIorExpression.First);

        Expression e = ParseIorExpression.Parse(follow.Union(current), t);        

        t.Accept(Token.RightParenthesis, follow);

        ParserDebug.WriteLine(
          "Leaving 'ParseTransitionCondition'",
          ConsoleColor.Yellow
        );

        return e;
      }
    }
  }
}
